#!/usr/bin/python2.4
#
#Copyright 2009 Google Inc.
#
#Licensed under the Apache License, Version 2.0 (the "License");
#you may not use this file except in compliance with the License.
#You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
#Unless required by applicable law or agreed to in writing, software
#distributed under the License is distributed on an "AS IS" BASIS,
#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#See the License for the specific language governing permissions and
#limitations under the License.
#

"""Provides class to maintain metadata in memory.

This module provides MetadataInMemory class which is singleton implementation of
storing metadata rows in memory and uploading metadata to db in batches.
"""

import logging
import constants
import db_utils


class Borg(object):
  """Implements Borg Design Pattern."""

  # Shared state of all the instances of the class that inherits this class.
  _shared_state = {}

  def __init__(self):
    self.__dict__ = self._shared_state


class MetadataInMemory(Borg):
  """This class is singleton implementation of in-memory metadata.

  This class stores singleton instance of class_Singleton. This class stores
  array of metadata rows. It also stores the max_set value, which is used for
  populating metadata in db in batches.
  """

  _max_set = constants.NO_OF_ROWS_IN_SET

  def __init__(self):
    # Call constructor of Super class.
    Borg.__init__(self)
    # Check whether _metadata_rows is already initialized.
    if '_metadata_rows' not in self.__dict__:
      self._metadata_rows = []

  def _GetNextSet(self):
    """This method returns the batch of metadata of size _max_set.

    This method gets _max_set no of metadata rows from _metadata_rows array.

    Returns:
      Set of _max_set no of metadata rows.
    """
    requested_set = self._metadata_rows[0:self._max_set]
    return requested_set

  def _DeleteSet(self):
    """This methd deletes the batch of metadata of size _max_set.

    This method deletes current batch of _max_set number of rows of metadata
    from _metadata_rows array so that next time next _max_set rows
    will be dispatched by _GetNextSet call.
    """
    del self._metadata_rows[0:self._max_set]

  def AddRow(self, metadata):
    """Adds metadata row in metadata_rows array.

    Args:
      metadata: Instance of class Metadata.
    """
    self._metadata_rows.append(metadata)

  def Clear(self):
    """Clears the metadata rows stored in memory."""
    del self._metadata_rows[0:]

  def IsEmpty(self):
    """Checks if metadata_rows array is empty.

    Returns:
      True if empty; False otherwise.
    """
    if not self._metadata_rows:
      return True
    return False

  def GetNumberOfRemainingRows(self):
    """Get number of rows in _metadata_rows.

    Returns:
      Length of _metadata_rows array.
    """
    return len(self._metadata_rows)

  def PopulateMetadataInDb(self):
    """Poulates metadata from metadata_rows in db in batches of _max_set.

    Returns:
      0 if metadata_rows is empty
      Number of rows uploaded to db, which is usually _max_set.
    """
    db_handler = db_utils.DbHandler()
    # Get max_set rows
    status = self.IsEmpty()
    if status:
      return 0
    rows = self._GetNextSet()
    if rows:
      db_handler.AddBulkMetadata(rows)
      self._DeleteSet()
    return len(rows)

  def UpdateMetadata(self, level1_map, level2_map, level3_map):
    """Updates the metadata with changes specified in level maps.

    This method updates metadata for the changes specified in level1_map,
    level2_map and level3_map. These maps are nothing but old value to new
    value mapping.  These new values are specified by the user during verify
    metadata phase. Simultaneously this method also creates page tree nodes.

    Args:
      level1_map: Dictionary object specifying old value to new value mapping
      for level1 unique values.
      level2_map: Dictionary object specifying old value to new value mapping
      for level2 unique values.
      level3_map: Dictionary object specifying old value to new value mapping
      for level3 unique values.
    """
    logging.debug('Updating metadata in memory with the changes suggested')
    i = 2
    count = len(self._metadata_rows)
    while i < count:
      metadata = self._metadata_rows[i]
      if metadata.sorting_column1 in level1_map:
        new_val = level1_map[metadata.sorting_column1]
        self._metadata_rows[i].sorting_column1 = new_val
      if metadata.sorting_column2 in level2_map:
        new_val = level2_map[metadata.sorting_column2]
        self._metadata_rows[i].sorting_column2 = new_val
      if metadata.sorting_column3 in level3_map:
        new_val = level3_map[metadata.sorting_column3]
        self._metadata_rows[i].sorting_column3 = new_val
      i += 1
